Passed
Push — master ( d344b9...f2ff62 )
by EMP
01:07
created

main.js ➔ addMessages   F

Complexity

Conditions 14

Size

Total Lines 48
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 32
dl 0
loc 48
rs 3.6
c 0
b 0
f 0
cc 14

How to fix   Complexity   

Complexity

Complex classes like main.js ➔ addMessages often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"use strict";
2
3
sodium.ready.then(function() {
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
4
5
const ae = new AllEars(function(ok) {
1 ignored issue
show
Bug introduced by
The variable AllEars seems to be never declared. If this is a global, consider adding a /** global: AllEars */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
6
	if (ok) {
7
		document.getElementById("txt_skey").style.background = "#466";
8
		document.getElementById("txt_skey").maxLength = "64";
9
	} else {
10
		console.log("Failed to load All-Ears");
11
	}
12
});
13
14
function TabState(cur, max, btnDele, btnUpdt) {
15
	this.cur = cur;
16
	this.max = max;
17
	this.btnDele = btnDele;
18
	this.btnUpdt = btnUpdt;
19
}
20
21
const tabs = [
22
	new TabState(0, 0, false, true), // Inbox
23
	new TabState(0, 0, false, true), // Outbx
24
	new TabState(0, 2, true, false), // Write
25
	new TabState(0, 2, false, false), // Notes
26
	new TabState(0, 3, false, true) // Tools
27
];
28
29
let showHeaders = false;
30
31
let tab = 0;
32
const TAB_INBOX = 0;
33
const TAB_OUTBX = 1;
0 ignored issues
show
Unused Code introduced by
The constant TAB_OUTBX seems to be never used. Consider removing it.
Loading history...
34
const TAB_WRITE = 2;
35
const TAB_NOTES = 3;
36
const TAB_TOOLS = 4;
37
38
// Helper functions
39
function getCountryName(countryCode) {
40
	const opts = document.getElementById("gatekeeper_country");
41
42
	for (let i = 0; i < opts.length; i++) {
43
		if (opts[i].value === countryCode) {
44
			return opts[i].textContent;
45
		}
46
	}
47
48
	return "Unknown countrycode: " + countryCode;
49
}
50
51
function getCountryFlag(countryCode) {
52
	return sodium.to_string(new Uint8Array([
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
53
		240, 159, 135, 166 + countryCode.codePointAt(0) - 65,
54
		240, 159, 135, 166 + countryCode.codePointAt(1) - 65
55
	]));
56
}
57
58
function getMsgId(num) {
59
	let i;
60
	if (ae.GetExtMsgHeaders(num).toLowerCase().slice(0, 11) === "message-id:") {
61
		i = 0;
62
	} else {
63
		i = ae.GetExtMsgHeaders(num).toLowerCase().indexOf("\nmessage-id:");
64
		if (i < 1) return "ERR";
65
		i++;
66
	}
67
68
	const x = ae.GetExtMsgHeaders(num).slice(i + 11).trim();
69
	if (x[0] !== "<") return "ERR2";
70
	return x.slice(1, x.indexOf(">"));
71
}
72
73
function clearDisplay() {
74
	let el = document.getElementById("midright").getElementsByTagName("img");
75
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("audio");
76
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("video");
77
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("embed");
78
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("iframe");
79
	if (el.length !== 1) return;
80
81
	URL.revokeObjectURL(el[0].src);
1 ignored issue
show
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
82
	el[0].remove();
83
}
84
85
function displayFile(num) {
86
	clearDisplay();
87
88
	document.getElementById("midright").scroll(0, 0);
89
	document.getElementById("btn_reply").disabled = true;
90
	document.getElementById("btn_mdele").disabled = true;
91
92
	document.getElementById("midright").children[0].hidden = true;
93
	document.getElementById("midright").children[1].textContent = ae.GetUplMsgTitle(num);
94
95
	switch (ae.GetUplMsgType(num)) {
96
		case "text": {
97
			document.getElementById("midright").children[2].hidden = false;
98
			document.getElementById("midright").children[2].textContent = sodium.to_string(ae.GetUplMsgBody(num));
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
99
		break;}
100
101
		case "image": {
102
			document.getElementById("midright").children[2].hidden = true;
103
			const img = document.createElement("img");
104
			img.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
2 ignored issues
show
Bug introduced by
The variable Blob seems to be never declared. If this is a global, consider adding a /** global: Blob */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
105
			document.getElementById("midright").appendChild(img);
106
107
			img.onclick = function() {
108
				if (!document.fullscreen)
109
					img.requestFullscreen();
110
				else
111
					document.exitFullscreen();
112
			};
113
		break;}
114
115
		case "audio": {
116
			document.getElementById("midright").children[2].hidden = true;
117
			const el = document.createElement("audio");
118
			el.controls = "controls";
119
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
120
			document.getElementById("midright").appendChild(el);
121
		break;}
122
123
		case "video": {
124
			document.getElementById("midright").children[2].hidden = true;
125
			const el = document.createElement("video");
126
			el.controls = "controls";
127
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
128
			document.getElementById("midright").appendChild(el);
129
		break;}
130
131
		case "pdf": {
132
			document.getElementById("midright").children[2].hidden = true;
133
			const el = document.createElement("embed");
134
			el.type = "application/pdf";
135
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
136
			document.getElementById("midright").appendChild(el);
137
		break;}
138
139
		case "html": {
140
			document.getElementById("midright").children[2].hidden = true;
141
			const el = document.createElement("iframe");
142
			el.allow = "";
143
			el.sandbox = "";
144
			el.csp = "base-uri 'none'; child-src 'none'; connect-src 'none'; default-src 'none'; font-src 'none'; form-action 'none'; frame-ancestors 'none'; frame-src 'none'; img-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'none'; style-src 'none'; worker-src 'none';";
145
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer], {type: "text/html"}));
146
			document.getElementById("midright").appendChild(el);
147
		break;}
148
	}
149
}
150
151
function displayMsg(isInt, num) {
152
	clearDisplay();
153
154
	document.getElementById("midright").scroll(0, 0);
155
156
	const ts = isInt? ae.GetIntMsgTime(num) : ae.GetExtMsgTime(num);
157
158
	document.getElementById("btn_reply").disabled = false;
159
	document.getElementById("btn_reply").onclick = function() {
160
		document.getElementById("write_recv").value = isInt? ae.GetIntMsgFrom(num) : ae.GetExtMsgFrom(num);
161
		document.getElementById("write_subj").value = "Re: " + (isInt ? ae.GetIntMsgTitle(num) : ae.GetExtMsgTitle(num));
162
		document.getElementById("write_rply").textContent = (isInt? "" : getMsgId(num));
163
		document.getElementById("btn_write").click();
164
		document.getElementById("div_write_1").hidden = false;
165
		document.getElementById("div_write_2").hidden = true;
166
		document.getElementById("write_body").focus();
167
		for (const opt of document.getElementById("write_from").options) {
168
			if (opt.value === (isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgTo(num))) {
169
				opt.selected = true;
170
			}
171
		}
172
	};
173
174
	document.getElementById("btn_mdele").disabled = false;
175
	document.getElementById("btn_mdele").onclick = function() {
176
		this.blur();
177
178
		ae.Message_Delete(isInt? ae.GetIntMsgIdHex(num) : ae.GetExtMsgIdHex(num), function(success) {
179
			if (!success) console.log("Failed delete");
180
		});
181
	};
182
183
	document.getElementById("midright").children[0].hidden = false;
184
	document.getElementById("midright").children[2].hidden = false;
185
186
	if (isInt) {
187
		document.getElementById("midright").children[1].textContent = ae.GetIntMsgTitle(num);
188
		document.getElementById("midright").children[2].textContent = ae.GetIntMsgBody(num);
189
	} else {
190
		document.getElementById("midright").children[2].innerHTML = "";
191
192
		const headers = document.createElement("p");
193
		headers.textContent = ae.GetExtMsgHeaders(num);
194
		headers.className = "mono";
195
		headers.hidden = !showHeaders;
196
		document.getElementById("midright").children[2].appendChild(headers);
197
198
		const body = document.createElement("p");
199
		body.textContent = ae.GetExtMsgBody(num);
200
		document.getElementById("midright").children[2].appendChild(body);
201
202
		document.getElementById("midright").children[1].textContent = ae.GetExtMsgTitle(num);
203
		document.getElementById("midright").children[1].onclick = function() {showHeaders = !showHeaders; headers.hidden = !showHeaders;};
204
		document.getElementById("midright").children[1].style.cursor = "pointer";
205
	}
206
207
	document.getElementById("readmsg_to").textContent = isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgTo(num);
208
	document.getElementById("readmsg_date").children[0].textContent = new Date(ts * 1000).toISOString().slice(0, 19).replace("T", " ");
209
210
	if (!isInt) {
211
		document.getElementById("readmsg_ip").hidden = false;
212
		document.getElementById("readmsg_country").hidden = false;
213
		document.getElementById("readmsg_tls").hidden = false;
214
		document.getElementById("readmsg_greet").hidden = false;
215
		document.getElementById("readmsg_timing").hidden = false;
216
		document.getElementById("readmsg_envfrom").hidden = false;
217
218
		const cc = ae.GetExtMsgCountry(num);
219
220
		document.getElementById("readmsg_ip").children[0].textContent = ae.GetExtMsgIp(num);
221
		document.getElementById("readmsg_country").textContent = getCountryFlag(cc) + " " + getCountryName(cc);
222
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetExtMsgTLS(num);
223
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetExtMsgGreet(num);
224
		document.getElementById("readmsg_envfrom").textContent = ae.GetExtMsgFrom(num);
225
226
		let flagText = "";
227
		if (!ae.GetExtMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
228
		if (!ae.GetExtMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
229
		if (!ae.GetExtMsgFlagPExt(num)) flagText += "<abbr title=\"The sender did not use the Extended (ESMTP) protocol\">SMTP</abbr> ";
230
		if (!ae.GetExtMsgFlagQuit(num)) flagText += "<abbr title=\"The sender did not issue the required QUIT command\">QUIT</abbr> ";
231
		if (ae.GetExtMsgFlagRare(num)) flagText += "<abbr title=\"The sender issued unusual command(s)\">RARE</abbr> ";
232
		if (ae.GetExtMsgFlagFail(num)) flagText += "<abbr title=\"The sender issued invalid command(s)\">FAIL</abbr> ";
233
		if (ae.GetExtMsgFlagPErr(num)) flagText += "<abbr title=\"The sender violated the protocol\">PROT</abbr> ";
234
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
235
	} else {
236
		document.getElementById("readmsg_ip").hidden = true;
237
		document.getElementById("readmsg_country").hidden = true;
238
		document.getElementById("readmsg_tls").hidden = true;
239
		document.getElementById("readmsg_greet").hidden = true;
240
		document.getElementById("readmsg_timing").hidden = true;
241
		document.getElementById("readmsg_envfrom").hidden = true;
242
243
		let symbol = "<span title=\"Invalid level\">&#x26a0;</span>";
244
		if (ae.GetIntMsgFrom(num) === "system") {if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"System\">&#x1f162;</span>";} // S (System)
245
		else if (ae.GetIntMsgLevel(num) === 0) symbol = "<span title=\"Level 0 User\">&#x1f10c;</span>"; // 0
246
		else if (ae.GetIntMsgLevel(num) === 1) symbol = "<span title=\"Level 1 User\">&#x278a;</span>"; // 1
247
		else if (ae.GetIntMsgLevel(num) === 2) symbol = "<span title=\"Level 2 User\">&#x278b;</span>"; // 2
248
		else if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"Administrator\">&#x1f150;</span>"; // A (Admin)
249
		document.getElementById("readmsg_from").innerHTML = symbol + " " + ae.GetIntMsgFrom(num);
250
251
		let flagText = "";
252
		if (!ae.GetIntMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
253
		if (!ae.GetIntMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
254
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
255
	}
256
}
257
258
// Interface
259
function addMsg(isInt, i) {
260
	const row = document.getElementById("tbl_inbox").insertRow(-1);
261
	const cellTime = row.insertCell(-1);
262
	const cellSubj = row.insertCell(-1);
263
	const cellSnd1 = row.insertCell(-1);
264
	const cellSnd2 = row.insertCell(-1);
265
266
	const ts = isInt? ae.GetIntMsgTime(i) : ae.GetExtMsgTime(i);
267
	cellTime.setAttribute("data-ts", ts);
268
	cellTime.textContent = new Date(ts * 1000).toISOString().slice(0, 10);
269
270
	cellSubj.textContent = isInt? ae.GetIntMsgTitle(i) : ae.GetExtMsgTitle(i);
271
272
	if (isInt) {
273
		cellSnd1.textContent = ae.GetIntMsgFrom(i);
274
		cellSnd1.className = (ae.GetIntMsgFrom(i).length === 16) ? "mono" : "";
275
	} else {
276
		const from1 = ae.GetExtMsgFrom(i);
277
		const from2 = from1.substring(from1.indexOf("@") + 1);
278
		const cc = ae.GetExtMsgCountry(i);
279
280
		cellSnd1.textContent = from1.substring(0, from1.indexOf("@"));
281
282
		const flag = document.createElement("abbr");
283
		flag.textContent = getCountryFlag(cc);
284
		flag.title = getCountryName(cc);
285
		cellSnd2.appendChild(flag);
286
287
		const fromText = document.createElement("span");
288
		fromText.textContent = " " + from2;
289
		cellSnd2.appendChild(fromText);
290
	}
291
292
	row.onclick = function() {
293
		displayMsg(isInt, i);
294
	};
295
}
296
297
function getRowsPerPage() {
298
	const tbl = document.getElementById("tbl_inbox");
299
	tbl.innerHTML = "";
300
	const row = tbl.insertRow(-1);
301
	const cell = row.insertCell(-1);
302
	cell.textContent = "0";
303
304
	const rowsPerPage = Math.floor(getComputedStyle(document.getElementById("div_inbox")).height.replace("px", "") / getComputedStyle(document.querySelector("#tbl_inbox > tbody > tr:first-child")).height.replace("px", "")) - 1; // -1 allows space for 'load more'
305
	tbl.innerHTML = "";
306
	return rowsPerPage;
307
}
308
309
function addMessages() {
310
	const rowsPerPage = getRowsPerPage();
311
	let skipMsgs = rowsPerPage * tabs[TAB_INBOX].cur;
312
313
	const maxExt = ae.GetExtMsgCount();
314
	const maxInt = ae.GetIntMsgCount();
315
316
	tabs[TAB_INBOX].max = Math.floor((maxExt + maxInt) / rowsPerPage);
317
318
	let numExt = 0;
319
	let numInt = 0;
320
	let numAdd = 0;
321
322
	while (numAdd < rowsPerPage) {
323
		const tsInt = (numInt < maxInt) ? ae.GetIntMsgTime(numInt) : -1;
324
		const tsExt = (numExt < maxExt) ? ae.GetExtMsgTime(numExt) : -1;
325
		if (tsInt === -1 && tsExt === -1) break;
326
327
		if (tsInt !== -1 && (tsExt === -1 || tsInt > tsExt)) {
328
			if (skipMsgs > 0) skipMsgs--; else {addMsg(true, numInt); numAdd++;}
329
			numInt++;
330
		} else if (tsExt !== -1) {
331
			if (skipMsgs > 0) skipMsgs--; else {addMsg(false, numExt); numAdd++;}
332
			numExt++;
333
		}
334
	}
335
336
	if (ae.GetReadyMsgBytes() < ae.GetTotalMsgBytes()) {
337
		const inbox = document.getElementById("tbl_inbox");
338
		const row = inbox.insertRow(-1);
339
		const cell = row.insertCell(-1);
340
		cell.textContent = "Load more (" + (ae.GetTotalMsgBytes() - ae.GetReadyMsgBytes()) / 1024 + " KiB left)";
341
342
		row.onclick = function() {
343
			this.onclick = "";
344
345
			ae.Message_Browse(false, function(successBrowse) {
346
				document.getElementById("tbl_inbox").style.opacity = 1;
347
348
				if (successBrowse) {
349
					addMessages();
350
					addUploads();
351
					if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
352
				}
353
			});
354
		};
355
	}
356
}
357
358
function addUploads() {
359
	const tbl = document.getElementById("tbd_uploads");
360
	tbl.innerHTML = "";
361
362
	for (let i = 0; i < ae.GetUplMsgCount(); i++) {
363
		const row = tbl.insertRow(-1);
364
		let cell;
365
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetUplMsgTime(i) * 1000).toISOString().slice(0, 10);
366
367
		cell = row.insertCell(-1); cell.textContent = ae.GetUplMsgTitle(i);
368
		cell.onclick = function() {displayFile(this.parentElement.rowIndex - 1);};
369
370
		cell = row.insertCell(-1); cell.textContent = ""; // Format
371
		cell = row.insertCell(-1); cell.textContent = ""; // Size
372
373
		cell = row.insertCell(-1); cell.innerHTML = "<button data-msgid=\"" + ae.GetUplMsgIdHex(i) + "\" type=\"button\">X</button>";
374
		cell.children[0].onclick = function() {
375
			const tr = this.parentElement.parentElement;
376
			ae.Message_Delete(this.getAttribute("data-msgid"), function(success) {
377
				if (success) tr.remove();
378
			});
379
		};
380
	}
381
}
382
383
function updateAddressCounts() {
384
	document.getElementById("limit_normal").textContent = (ae.GetAddressCountNormal() + "/" + ae.GetAddressLimitNormal(ae.GetUserLevel())).padStart(ae.GetAddressLimitNormal(ae.GetUserLevel()) > 9 ? 5 : 1);
385
	document.getElementById("limit_shield").textContent = (ae.GetAddressCountShield() + "/" + ae.GetAddressLimitShield(ae.GetUserLevel())).padStart(ae.GetAddressLimitShield(ae.GetUserLevel()) > 9 ? 5 : 1);
386
	document.getElementById("limit_total").textContent = ((ae.GetAddressCountNormal() + ae.GetAddressCountShield()) + "/" + ae.GetAddrPerUser()).padStart(5);
387
}
388
389
function adjustLevel(pubkey, level, c) {
390
	const fs = document.getElementById("fs_accs");
391
	fs.disabled = true;
392
393
	ae.Account_Update(pubkey, level, function(success) {
394
		fs.disabled = false;
395
396
		if (success) {
397
			c[4].textContent = level;
398
			c[5].children[0].disabled = (level === 3);
399
			c[6].children[0].disabled = (level === 0);
400
		}
401
	});
402
}
403
404
function addAccountToTable(i) {
405
	const tblAccs = document.getElementById("tbd_accs");
406
	const row = tblAccs.insertRow(-1);
407
	let cell;
408
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserPkHex(i);
409
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSpace(i);
410
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserNAddr(i);
411
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSAddr(i);
412
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserLevel(i);
413
414
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">+</button>";
415
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent) + 1, c);};
416
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 3);
417
418
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">&minus;</button>";
419
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent) - 1, c);};
420
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 0);
421
422
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">X</button>";
423
	cell.children[0].onclick = function() {
424
		const tr = this.parentElement.parentElement;
425
		ae.Account_Delete(tr.cells[0].textContent, function(success) {
426
			if (success) tr.remove();
427
		});
428
	};
429
}
430
431
function reloadAccount() {
432
	// Limits
433
	const tblLimits = document.getElementById("tbl_limits");
434
	for (let i = 0; i < 4; i++) {
435
		tblLimits.rows[i].cells[1].children[0].value = ae.GetStorageLimit(i) + 1;
436
		tblLimits.rows[i].cells[2].children[0].value = ae.GetAddressLimitNormal(i);
437
		tblLimits.rows[i].cells[3].children[0].value = ae.GetAddressLimitShield(i);
438
	}
439
440
	// Accounts
441
	const tblAccs = document.getElementById("tbd_accs");
442
443
	// All: Our account
444
	const row = tblAccs.insertRow(-1);
445
	let cell;
446
	cell = row.insertCell(-1); cell.textContent = ae.GetUserPkHex();
447
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.GetTotalMsgBytes() / 1024);
448
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountNormal();
449
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountShield();
450
	cell = row.insertCell(-1); cell.textContent = ae.GetUserLevel();
451
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\" disabled=\"disabled\">+</button>";
452
453
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_downme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">&minus;</button>";
454
	cell.children[0].onclick = function() {
455
		const newLevel = parseInt(row.cells[4].textContent) - 1;
456
		ae.Account_Update(ae.GetUserPkHex(), newLevel, function(success) {
457
			if (success) row.cells[4].textContent = newLevel;
458
		});
459
	};
460
461
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_killme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">X</button>";
462
	cell.children[0].onclick = function() {
463
		ae.Account_Delete(ae.GetUserPkHex(), function(success) {
464
			if (success) row.remove();
465
		});
466
	};
467
468
	// Admin: Other accounts
469
	if (ae.IsUserAdmin()) {
470
		for (let i = 0; i < ae.Admin_GetUserCount(); i++) {
471
			addAccountToTable(i);
472
		}
473
	}
474
475
	document.getElementById("txt_reg").disabled = !ae.IsUserAdmin();
476
	document.getElementById("btn_reg").disabled = !ae.IsUserAdmin();
477
478
	// Contacts
479
	for (let i = 0; i < ae.GetContactCount(); i++) {
480
		addContact(
481
			ae.GetContactMail(i),
482
			ae.GetContactName(i),
483
			ae.GetContactNote(i)
484
		);
485
	}
486
487
	// Addresses
488
	for (let i = 0; i < ae.GetAddressCount(); i++) {
489
		addAddress(i);
490
	}
491
492
	updateAddressCounts();
493
}
494
495 View Code Duplication
function deleteAddress(addr) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
496
	let btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
497
	for (let i = 0; i < btns.length; i++) btns[i].disabled = true;
498
499
	let addressToDelete = -1;
500
501
	for (let i = 0; i < ae.GetAddressCount(); i++) {
502
		if (addr === ae.GetAddress(i)) {
503
			addressToDelete = i;
504
			break;
505
		}
506
	}
507
508
	if (addressToDelete === -1) return;
509
510
	ae.Address_Delete(addressToDelete, function(success) {
511
		if (success) {
512
			document.getElementById("tbl_addrs").deleteRow(addressToDelete);
513
			document.getElementById("write_from").remove(addressToDelete);
514
			updateAddressCounts();
515
516
			if (ae.GetAddressCountNormal() < ae.GetAddressLimitNormal(ae.GetUserLevel())) document.getElementById("btn_address_create_normal").disabled = false;
517
			if (ae.GetAddressCountShield() < ae.GetAddressLimitShield(ae.GetUserLevel())) document.getElementById("btn_address_create_shield").disabled = false;
518
519
			ae.Private_Update(function(success2) {
520
				if (!success2) console.log("Failed to update the Private field");
521
522
				btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
523
				for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
524
			});
525
		} else {
526
			console.log("Failed to delete address");
527
528
			btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
529
			for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
530
		}
531
	});
532
}
533
534 View Code Duplication
function shieldMix(addr) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
535
	let newAddr = "";
536
537
	for (let i = 0; i < 16; i++) {
538
		switch (addr.charAt(i)) {
539
			case '1':
540
				newAddr += "1iIlL".charAt(Math.floor(Math.random() * 5));
541
				break;
542
			case '0':
543
				newAddr += "0oO".charAt(Math.floor(Math.random() * 3));
544
				break;
545
			case 'w':
546
				newAddr += "VvWw".charAt(Math.floor(Math.random() * 4));
547
				break;
548
			default:
549
				newAddr += (Math.random() > 0.5) ? addr.charAt(i) : addr.charAt(i).toUpperCase();
550
		}
551
	}
552
553
	return newAddr;
554
}
555
556
function addAddress(num) {
557
	const addrTable = document.getElementById("tbl_addrs");
558
	const row = addrTable.insertRow(-1);
559
	const cellAddr = row.insertCell(-1);
560
	const cellChk1 = row.insertCell(-1);
561
	const cellChk2 = row.insertCell(-1);
562
	const cellChk3 = row.insertCell(-1);
563
	const cellBtnD = row.insertCell(-1);
564
565
	cellAddr.textContent = ae.GetAddress(num);
566
	cellAddr.onclick = function() {
567
		if (cellAddr.textContent.length === 16)
568
			navigator.clipboard.writeText(shieldMix(cellAddr.textContent) + "@" + ae.GetDomainEml());
1 ignored issue
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
569
		else
570
			navigator.clipboard.writeText(cellAddr.textContent + "@" + ae.GetDomainEml());
571
	};
572
573
	cellChk1.innerHTML = ae.GetAddressAccExt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
574
	cellChk2.innerHTML = ae.GetAddressAccInt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
575
	cellChk3.innerHTML = ae.GetAddressUse_Gk(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
576
577
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
578
	cellBtnD.onclick = function() {deleteAddress(cellAddr.textContent);};
579
580
	const opt = document.createElement("option");
581
	opt.value = cellAddr.textContent;
582
	opt.textContent = cellAddr.textContent + "@" + ae.GetDomainEml();
583
	document.getElementById("write_from").appendChild(opt);
584
}
585
586
document.getElementById("btn_dele").onclick = function() {
587
	this.blur();
588
589
	if (tab === TAB_WRITE) {
590
		tabs[tab].cur = 0;
591
		updateTab();
592
593
		document.getElementById("write_recv").value = "";
594
		document.getElementById("write_subj").value = "";
595
		document.getElementById("write_body").value = "";
596
597
		document.getElementById("write_recv").focus();
598
	}
599
};
600
601
document.getElementById("btn_updt").onclick = function() {
602
	const btn = this;
603
	btn.disabled = true;
604
	btn.blur();
605
606
	if (tab === TAB_INBOX) {
607
		document.getElementById("tbl_inbox").style.opacity = 0.5;
608
609
		ae.Message_Browse(true, function(successBrowse) {
610
			document.getElementById("tbl_inbox").style.opacity = 1;
611
612
			if (successBrowse) {
613
				addMessages();
614
				addUploads();
615
				btn.disabled = false;
616
			} else {
617
				console.log("Failed to refresh");
618
				btn.disabled = false;
619
			}
620
		});
621
	}
622
};
623
624
function addContact(mail, name, note) {
625
	const tbl = document.getElementById("tbl_ctact");
626
	const row = tbl.insertRow(-1);
627
	const cellMail = row.insertCell(-1);
628
	const cellName = row.insertCell(-1);
629
	const cellNote = row.insertCell(-1);
630
	const cellBtnD = row.insertCell(-1);
631
632
	cellMail.textContent = mail;
633
	cellName.textContent = name;
634
	cellNote.textContent = note;
635
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
636
637
	cellMail.contentEditable = true;
638
	cellName.contentEditable = true;
639
	cellNote.contentEditable = true;
640
641
	cellBtnD.onclick = function() {row.remove();};
642
}
643
644
document.getElementById("btn_newcontact").onclick = function() {
645
	addContact("", "", "");
646
};
647
648
document.getElementById("btn_savecontacts").onclick = function() {
649
	while (ae.GetContactCount() > 0) {
650
		ae.DeleteContact(0);
651
	}
652
653
	for (const row of document.getElementById("tbl_ctact").rows) {
654
		ae.AddContact(row.cells[0].textContent, row.cells[1].textContent, row.cells[2].textContent);
655
	}
656
657
	const btn = this;
658
	btn.disabled = true;
659
660
	ae.Private_Update(function(success) {
661
		btn.disabled = false;
662
663
		if (!success) {
664
			console.log("Failed contacts update");
665
		}
666
	});
667
};
668
669
function updateTab() {
670
	switch (tab) {
671
		case TAB_INBOX:
672
			addMessages();
673
		break;
674
675
		case TAB_WRITE:
676
			switch (tabs[tab].cur) {
677
				case 0: // Write
678
					document.getElementById("div_write_1").hidden = false;
679
					document.getElementById("div_write_2").hidden = true;
680
					document.getElementById("write_body").focus();
681
				break;
682
683
				case 1: // Verify
684
					ae.Address_Lookup(document.getElementById("write_recv").value, function(pk) {
685
						if (pk) {
686
							document.getElementById("div_write_1").hidden = true;
687
							document.getElementById("div_write_2").hidden = false;
688
689
							document.getElementById("write2_from").textContent = document.getElementById("write_from").value + "@" + ae.GetDomainEml();
690
							document.getElementById("write2_recv").textContent = document.getElementById("write_recv").value;
691
							document.getElementById("write2_pkey").textContent = sodium.to_hex(pk);
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
692
693
							document.getElementById("write2_subj").textContent = document.getElementById("write_subj").value;
694
							document.getElementById("write2_rply").textContent = document.getElementById("write_rply").textContent;
695
							document.getElementById("write2_body").textContent = document.getElementById("write_body").value;
696
						} else {
697
							console.log("Failed lookup");
698
						}
699
					});
700
				break;
701
702
				case 2: // Send
703
					ae.Message_Create(
704
						document.getElementById("write_subj").value,
705
						document.getElementById("write_body").value,
706
						document.getElementById("write_from").value,
707
						document.getElementById("write_recv").value,
708
						document.getElementById("write_rply").textContent,
709
						(document.getElementById("write2_recv").textContent.indexOf("@") > 0) ? null : sodium.from_hex(document.getElementById("write2_pkey").textContent),
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
710
						function(success) {
711
							if (success) {
712
								console.log("Sent ok");
713
							} else {
714
								console.log("Failed sending");
715
							}
716
						}
717
					);
718
				break;
719
			}
720
		break;
721
722
		case TAB_NOTES:
723
			for (let i = 0; i <= tabs[tab].max; i++) {
724
				document.getElementById("div_notes").children[i].hidden = (i !== tabs[tab].cur);
725
			}
726
		break;
727
728
		case TAB_TOOLS:
729
			for (let i = 0; i <= tabs[tab].max; i++) {
730
				document.getElementById("div_tools").children[i].hidden = (i !== tabs[tab].cur);
731
			}
732
		break;
733
	}
734
735
	document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
736
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
737
}
738
739
document.getElementById("btn_left").onclick = function() {
740
	tabs[tab].cur--;
741
	if (tabs[tab].cur === 0) this.disabled = true;
742
	if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
743
	updateTab();
744
	this.blur();
745
};
746
747
document.getElementById("btn_rght").onclick = function() {
748
	tabs[tab].cur++;
749
	if (tabs[tab].cur === tabs[tab].max) this.disabled = true;
750
	document.getElementById("btn_left").disabled = false;
751
	updateTab();
752
	this.blur();
753
};
754
755
const buttons = document.querySelector("#main1 > .top").getElementsByTagName("button");
756
for (let i = 0; i < buttons.length; i++) {
757
	buttons[i].onclick = function() {
758
		tab = i;
759
760
		for (let j = 0; j < buttons.length; j++) {
761
			document.querySelector("#main1 > .mid").children[j].hidden = (tab !== j);
762
			buttons[j].disabled = (tab === j);
763
		}
764
765
		document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
0 ignored issues
show
Bug introduced by
The variable tab is changed as part of the for loop for example by i on line 758. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
766
		document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
767
		document.getElementById("btn_dele").disabled = !tabs[tab].btnDele;
768
		document.getElementById("btn_updt").disabled = !tabs[tab].btnUpdt;
769
	};
770
}
771
772
function addressCreate(addr) {
773
	const btnN = document.getElementById("btn_address_create_normal");
774
	const btnS = document.getElementById("btn_address_create_shield");
775
	btnN.disabled = true;
776
	btnS.disabled = true;
777
778
	ae.Address_Create(addr, function(success1) {
779
		if (success1) {
780
			ae.Private_Update(function(success2) {
781
				addAddress(ae.GetAddressCount() - 1);
782
				if (addr !== "SHIELD") document.getElementById("txt_address_create_normal").value = "";
783
				updateAddressCounts();
784
785
				if (!success2) console.log("Failed to update the Private field");
786
787
				if (ae.GetAddressCountNormal() < ae.GetAddressLimitNormal(ae.GetUserLevel())) btnN.disabled = false;
788
				if (ae.GetAddressCountShield() < ae.GetAddressLimitShield(ae.GetUserLevel())) btnS.disabled = false;
789
			});
790
		} else {
791
			console.log("Failed to add address");
792
793
			if (ae.GetAddressCountNormal() < ae.GetAddressLimitNormal(ae.GetUserLevel())) btnN.disabled = false;
794
			if (ae.GetAddressCountShield() < ae.GetAddressLimitShield(ae.GetUserLevel())) btnS.disabled = false;
795
		}
796
	});
797
}
798
799
document.getElementById("btn_address_create_normal").onclick = function() {
800
	if (ae.GetAddressCountNormal() >= ae.GetAddressLimitNormal(ae.GetUserLevel())) return;
801
802
	const txtNewAddr = document.getElementById("txt_address_create_normal");
803
	if (!txtNewAddr.reportValidity()) return;
804
805
	addressCreate(txtNewAddr.value);
806
};
807
808
document.getElementById("btn_address_create_shield").onclick = function() {
809
	if (ae.GetAddressCountShield() >= ae.GetAddressLimitShield(ae.GetUserLevel())) return;
810
811
	addressCreate("SHIELD");
812
};
813
814
document.getElementById("btn_reg").onclick = function() {
815
	const btn = document.getElementById("btn_reg");
816
	const txt = document.getElementById("txt_reg");
817
	if (!txt.reportValidity()) return;
818
	btn.disabled = true;
819
820
	ae.Account_Create(txt.value, function(success) {
821
		if (success) {
822
			addAccountToTable(ae.Admin_GetUserCount() - 1);
823
			txt.value = "";
824
		}
825
826
		btn.disabled = false;
827
	});
828
};
829
830
document.getElementById("chk_downme").onclick = function() {document.getElementById("btn_downme").disabled = !this.checked;};
831
document.getElementById("chk_killme").onclick = function() {document.getElementById("btn_killme").disabled = !this.checked;};
832
833
document.getElementById("btn_notepad_saveupl").onclick = function() {
834
	const np = document.getElementById("txt_notepad");
835
	np.disabled = true;
836
837
	let fname = prompt("Save as...", "Untitled");
0 ignored issues
show
Debugging Code Best Practice introduced by
The prompt UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
838
	if (!fname.endsWith(".txt")) fname += ".txt";
839
840
	ae.Message_Upload(fname, np.value, function(success) {
841
		if (success) {
842
			np.value = "";
843
			addUploads();
844
		}
845
846
		console.log("Failed to add text");
847
		np.disabled = false;
848
	});
849
};
850
851
document.getElementById("btn_upload").onclick = function() {
852
	const btn = this;
853
	const fileSelector = document.createElement("input");
854
	fileSelector.type = "file";
855
	fileSelector.click();
856
857
	fileSelector.onchange = function() {
858
		btn.disabled = true;
859
860
		const reader = new FileReader();
1 ignored issue
show
Bug introduced by
The variable FileReader seems to be never declared. If this is a global, consider adding a /** global: FileReader */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
861
		reader.onload = function() {
862
			ae.Message_Upload(fileSelector.files[0].name, new Uint8Array(reader.result), function(success) {
863
				if (success) {
864
					addUploads();
865
				} else {
866
					console.log("Failed upload");
867
				}
868
869
				btn.disabled = false;
870
			});
871
		};
872
873
		reader.readAsArrayBuffer(fileSelector.files[0]);
874
	};
875
};
876
877
document.getElementById("txt_skey").onkeyup = function(event) {
878
	if (event.key === "Enter") {
879
		event.preventDefault();
880
		document.getElementById("btn_enter").click();
881
	}
882
};
883
884
document.getElementById("btn_enter").onclick = function() {
885
	const txtSkey = document.getElementById("txt_skey");
886
	if (!txtSkey.reportValidity()) return;
887
888
	const btn = this;
889
	btn.disabled = true;
890
	document.getElementById("txt_skey").style.background = "#233";
891
892
	ae.SetKeys(txtSkey.value, function(successSetKeys) {
893
		if (successSetKeys) {
894
			ae.Account_Browse(0, function(successBrowse) {
895
				if (successBrowse) {
896
					txtSkey.value = "";
897
898
					reloadAccount();
899
					document.getElementById("div_begin").hidden = true;
900
					document.getElementById("div_main").style.display = "grid";
901
902
					document.getElementById("btn_updt").click();
903
				} else {
904
					console.log("Failed to enter");
905
					btn.disabled = false;
906
					document.getElementById("txt_skey").style.background = "#466";
907
					txtSkey.focus();
908
				}
909
			});
910
		} else {
911
			console.log("Invalid format for key");
912
			btn.disabled = false;
913
			document.getElementById("txt_skey").style.background = "#466";
914
			txtSkey.focus();
915
		}
916
	});
917
};
918
919
});
920